#pragma once
#include <boost/shared_ptr.hpp>

namespace grflib
{
	namespace engine
	{
        namespace core
		{
            ///////////////////////////////////////////////////
            //Traverse functor base class
            template<typename T, typename TPtr = boost::shared_ptr<T>>
            struct IMemberCaller
            {
                virtual HRESULT operator() (TPtr &) const = 0;
            };

            ///////////////////////////////////////////////////

            //delete traverse functor
            template<typename T, typename TPtr = boost::shared_ptr<T>>
            struct IDeleteFunc : public IMemberCaller<T, TPtr>
            {
                virtual HRESULT operator() (TPtr &pObj) const
                {
                    pObj.reset();
                    return S_OK;
                }
            };

            //////////////////////////////////////////////////////
			// Functors for traversing the scene
			//////////////////////////////////////////////////////
			
			//requires HRESULT T::OnRender(ID3D10Device *, D3DXMATRIX *, double fTime, float fElapsedTime, UINT pass)
			template<typename T = IRenderable>
			struct IRenderFunc : public IMemberCaller<T>
			{
				IRenderFunc(ID3D10Device *pd3dDevice, D3DXMATRIX *pWorldMat, double fTime, float fElapsedTime, UINT pass)
					: m_pd3dDevice(pd3dDevice), m_fTime(fTime), m_fElapsedTime(fElapsedTime),
					  m_pWorldMat(pWorldMat), m_pass(pass) {}
				void SetWorldMatPtr(D3DXMATRIX *pWorldMat) { m_pWorldMat = pWorldMat; }
                void SetPass(UINT pass) { m_pass = pass; }
				virtual HRESULT operator() (boost::shared_ptr<T> &pObj) const
				{ return pObj->OnRender(m_pd3dDevice, m_pWorldMat, m_fTime, m_fElapsedTime, m_pass); }

			protected:
				ID3D10Device *m_pd3dDevice;
				double m_fTime;
				float m_fElapsedTime;
				D3DXMATRIX *m_pWorldMat;
                UINT m_pass;
			};

			//requires HRESULT T::OnCreateDevice(ID3D10Device *, const DXGI_SURFACE_DESC*)
			template<typename T>
			struct ICreateFunc : public IMemberCaller<T>
			{
				ICreateFunc(ID3D10Device *pd3dDevice, const DXGI_SURFACE_DESC* pBufferSurfaceDesc)
					: m_pd3dDevice(pd3dDevice), m_pBufferSurfaceDesc(pBufferSurfaceDesc) {}
				virtual HRESULT operator() (boost::shared_ptr<T> &pObj) const
				{ return pObj->OnCreateDevice(m_pd3dDevice, m_pBufferSurfaceDesc); }

			protected:
				ID3D10Device *m_pd3dDevice;
				const DXGI_SURFACE_DESC *m_pBufferSurfaceDesc;
			};

			//requires HRESULT T::Release()
			template<typename T>
			struct IReleaseFunc : public IMemberCaller<T>
			{
				virtual HRESULT operator() (boost::shared_ptr<T> &pObj) const 
                { pObj->Release(); pObj.reset(); return S_OK; }
			};

			//requires T::OnDestroy()
			template<typename T>
			struct IDestroyFunc : public IMemberCaller<T>
			{
				virtual HRESULT operator() (boost::shared_ptr<T> &pObj) const 
                { pObj->OnDestroy(); return S_OK; }
			};

			//requires HRESULT T::OnResize(ID3D10Device *, IDXGISwapChain *, const DXGI_SURFACE_DESC *)
			template<typename T>
			struct IResizeFunc : public IMemberCaller<T>
			{
				IResizeFunc(ID3D10Device *pd3dDevice, IDXGISwapChain *pSwapChain, const DXGI_SURFACE_DESC *pBufferSurfaceDesc)
					: m_pd3dDevice(pd3dDevice), m_pSwapChain(pSwapChain), m_pBufferSurfaceDesc(pBufferSurfaceDesc) {}
				virtual HRESULT operator() (boost::shared_ptr<T> &pObj) const
				{ return pObj->OnResize(m_pd3dDevice, m_pSwapChain, m_pBufferSurfaceDesc); }

			protected:
				ID3D10Device *m_pd3dDevice;
				IDXGISwapChain *m_pSwapChain;
				const DXGI_SURFACE_DESC *m_pBufferSurfaceDesc;
			};

			//requires HRESULT T::OnResize(UINT width, UINT height)
            template<typename T>
            struct IResizeFunc1 : public IMemberCaller<T>
            {
                IResizeFunc1(UINT w, UINT h)
                    : m_width(w), m_height(h) {}
                virtual HRESULT operator() (boost::shared_ptr<T> &pObj) const
                { return pObj->OnResize(m_width, m_height); }

            protected:
                UINT m_width, m_height;
            };

			//requires T::FrameMove(double fTime, float fElapsedTime)
			template<typename T>
			struct IFrameMoveFunc : public IMemberCaller<T>
			{
				IFrameMoveFunc(double fTime, float fElapsedTime)
					: m_fTime(fTime), m_fElapsedTime(fElapsedTime) {}
				virtual HRESULT operator() (boost::shared_ptr<T> &pObj) const
				{ pObj->FrameMove(m_fTime, m_fElapsedTime); return S_OK; }

			protected:
				double m_fTime;
				float m_fElapsedTime;
			};

			//requires HRESULT T::MsgProc(HWND, UINT, WPARAM, LPARAM)
			template<typename T>
			struct IMsgFunc : public IMemberCaller<T>
			{
				IMsgFunc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
					: m_hWnd(hWnd), m_uMsg(uMsg), m_wParam(wParam), m_lParam(lParam) {}
				virtual HRESULT operator() (boost::shared_ptr<T> &pObj) const
				{
					bool ret = pObj->MsgProc(m_hWnd, m_uMsg, m_wParam, m_lParam); 
					return ret /*TRUE to block further processing*/ ? E_ABORT : S_OK; 
				}

			protected:
				HWND m_hWnd;
				UINT m_uMsg;
				WPARAM m_wParam;
				LPARAM m_lParam;
			};

		} //namespace core
	} //namespace engine
} //namespace grflib